/**
 * 
 */
package kernel_gui.gui.generic;


import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.NumberFormat;
import java.util.Locale;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import main.Kernel;

import kernel_gui.gui.GUIControl;

/**
 * @author Yoyo
 *
 */
public abstract class MillionaireFrame extends JFrame implements kernel_gui.interfaces.IGUI {

	/**
	 * 
	 */
	private static final long serialVersionUID = 8335993735122016362L;
	
	private Object syncme = new Object();
	private boolean waitingForAnswer = false;
	private int waitingForAnswer_RequesetID;
	private boolean answerIsPopulating = false;
	private CommandToExecute executeOnReturn = null;
	private CommandToExecute executeOnCancel = null;
	private CommandToExecute executeBeforeLongRequest = null;
	
	private MillionaireFrame returnToFrame = null;
	private int callingFrameRequestID;
	private boolean calledFromAnotherFrame = false;
	private Object answerToCallingFrame = null;
	
	private static int lastRequestID = 0;
	
	protected static Kernel kernel = null;
	protected JPanel LoadingPanel;
	protected JPanel MainPanel;
    protected javax.swing.JButton CancelButton;
	
    protected boolean XisBack = false;
    protected NumberFormat currency_nf = NumberFormat.getCurrencyInstance(Locale.US);
    protected NumberFormat numbers_nf = NumberFormat.getNumberInstance(Locale.US);
    
	
	protected void BackButtonActionPerformed(ActionEvent evt){
		/*do nothing*/
	}
	
	public MillionaireFrame(boolean XisBack){
		currency_nf.setMaximumFractionDigits(0);
		if(kernel == null){
			MillionaireFrame.kernel = GUIControl.getKernel();
		}
		
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		GUIControl.setCurrentVisibleFrame(this);
		
		if(!XisBack){/*Then X close*/
			this.addWindowListener(new WindowAdapter() { 
		        public void windowClosing(WindowEvent e) { 
		        	//Display confirm dialog 
		            int confirmed = JOptionPane.showConfirmDialog(MillionaireFrame.this, 
		                    "Are you sure you want to quit Millionaire?", "Confirm Quit", 
		                    JOptionPane.YES_NO_OPTION); 
		            
		            //Close if user confirmed 
		            if (confirmed == JOptionPane.YES_OPTION) 
		            {        
		            	//TODO - this is not a real requestID
		                kernel.exitMillioner(0);
		            }
		        } 
		    } 			    
			);	
		}else{
			this.addWindowListener(new WindowAdapter() { 
		        public void windowClosing(WindowEvent e) {
		        	/*just go back*/
		        	MillionaireFrame.this.BackButtonActionPerformed(null);
		        } 
		    } 			    
			);	
		
		}
	}
	
	/**
	 * @param executeOnCancel the executeOnCancel to set
	 */
	protected void setExecuteOnCancel(CommandToExecute executeOnCancel) {
		if(this.executeOnCancel == null)
			this.executeOnCancel = executeOnCancel;
		else
			throw new IllegalStateException();
	}



	/**
	 * @param executeOnLongRequest the executeOnLongRequest to set
	 */
	public void setExecuteBeforeLongRequest(CommandToExecute executeBeforeLongRequest) {
		if(this.executeBeforeLongRequest == null)
			this.executeBeforeLongRequest = executeBeforeLongRequest;
		else
			throw new IllegalStateException();
	}
	
	protected abstract void setWindowComponentsWhenCalledFromAnotherFrame();
	
	
	
	protected void openAnotherFrame(Class<? extends MillionaireFrame> classOfFrameToOpen, boolean closeCurrent, CommandToExecute executeOnReturn){
		MillionaireFrame frameToOpen = null;
			try {
				frameToOpen = classOfFrameToOpen.newInstance();
			} catch (InstantiationException e) {
				/*Shouldn't fail with the classes i use*/
			} catch (IllegalAccessException e) {
				/*Shouldn't fail with the classes i use*/
			}
		
		if(closeCurrent){
			/*Regular frame change*/
			frameToOpen.setVisible(true);
			this.setVisible(false);
			this.dispose();
		}else{
			
			/*Put in standby mode*/
			int requestID = this.executeBeforeLongRequest(executeOnReturn);
			/*Disable the cancel button, if the user wants he should close the other window*/
			if(this.CancelButton != null)
				this.CancelButton.setEnabled(false);
			
			/*Set and open the frame*/
			frameToOpen.setReturnToFrame(this, requestID);
			frameToOpen.setWindowComponentsWhenCalledFromAnotherFrame();
			frameToOpen.setVisible(true);
		}
	}
	
	protected void openAnotherFrame(Class<? extends MillionaireFrame> classOfFrameToOpen, boolean closeCurrent){
		openAnotherFrame(classOfFrameToOpen, closeCurrent, null);
	}
	
	protected void openPreCreatedFrame(MillionaireFrame ecf, CommandToExecute cte){
		/*Put in standby mode*/
		int requestID = this.executeBeforeLongRequest(cte);
		/*Disable the cancel button, if the user wants he should close the other window*/
		if(this.CancelButton != null)
			this.CancelButton.setEnabled(false);
		
		/*Set and open the frame*/
		ecf.setReturnToFrame(this, requestID);
		ecf.setVisible(true);
	}
	
	
	
	
	public void setAnswerToCallingFrame(Object answerToCallingFrame) {
		this.answerToCallingFrame = answerToCallingFrame;
	}

	protected void returnToCallingFrame(){
		if(returnToFrame == null)
			throw new IllegalStateException();
		
		returnToFrame.returnAnswer(callingFrameRequestID, answerToCallingFrame);
		if(returnToFrame.CancelButton != null)
			returnToFrame.CancelButton.setEnabled(true);
		
		GUIControl.setCurrentVisibleFrame(returnToFrame);
		returnToFrame.setVisible(true);
		this.setVisible(false);
		this.dispose();
	}

	
	/**
	 * @param returnToFrame the returnToFrame to set
	 */
	public void setReturnToFrame(MillionaireFrame returnToFrame, int requestID) {
		callingFrameRequestID = requestID;
		calledFromAnotherFrame = true;
		if(this.returnToFrame == null){
			this.returnToFrame = returnToFrame;
		}
		else
			throw new IllegalStateException();
	}

	protected void CancelButtonActionPerformed(java.awt.event.ActionEvent evt) {
    	if(this.answerIsPopulating){
    		JOptionPane.showMessageDialog(this, "Wait a sec, the answer is here!");
    		return;
    	}
    	
    	synchronized(syncme){
			waitingForAnswer = false;
			executeOnReturn = null;
	    	executeOnCancel.getRunnable().run();
	    	LoadingPanel.setVisible(false);
    	}
    }
	
	
	protected int executeBeforeLongRequest(CommandToExecute executeOnReturn){
		LoadingPanel.setVisible(true);
		this.executeBeforeLongRequest.getRunnable().run();
		
		this.executeOnReturn = executeOnReturn;
		waitingForAnswer = true;
		lastRequestID++;
		waitingForAnswer_RequesetID = lastRequestID;
		
		return waitingForAnswer_RequesetID;
	}

	protected boolean isCalledAsLongRequestFromOtherFrame(){
		return calledFromAnotherFrame;
	}
	
	
	
	/* (non-Javadoc)
	 * @see interfaces.IGUI#returnAnswer(int, java.lang.Object)
	 */
	@Override
	public void returnAnswer(int requestID, Object answer) {
		synchronized(syncme){
			this.answerIsPopulating = true;
			if(waitingForAnswer && requestID == waitingForAnswer_RequesetID){
				this.waitingForAnswer = false;
				if(executeOnReturn != null)
						SwingUtilities.invokeLater(executeOnReturn.getRunnable(answer));
				
					
			}
			this.answerIsPopulating = false;
			/*Cancel the loading window*/
			SwingUtilities.invokeLater(executeOnCancel.getRunnable());
			SwingUtilities.invokeLater(new Runnable() {
				
				@Override
				public void run() {
					LoadingPanel.setVisible(false);							
				}
			});
		}
		
		
	}

	/* (non-Javadoc)
	 * @see interfaces.IGUI#returnError(int, java.lang.String, boolean)
	 */
	@Override
	public void returnError(int requestID, String errorMSG, boolean fatalError) {
		if(fatalError){
			JOptionPane.showMessageDialog(this, errorMSG);
			/*this is not my requestID - but the window doesn't know*/
			kernel.exitMillioner(requestID);
		}else{
			synchronized(syncme){
				if(waitingForAnswer && requestID == waitingForAnswer_RequesetID){
					this.waitingForAnswer = false;
					JOptionPane.showMessageDialog(this, errorMSG);
	
				}
				
			}
			
			/*Cancel the loading window*/
			SwingUtilities.invokeLater(new Runnable() {
				
				@Override
				public void run() {
					CancelButtonActionPerformed(null);//TODO - check if it's always the right thing todo..
					
				}
			});
		}
	}
	
	
	protected abstract class CommandToExecute{
		
		public abstract void command(Object answer);
		
		public Runnable getRunnable(final Object answer){
			return new Runnable() {
				
				@Override
				public void run() {
					command(answer);					
				}
			};
		}
		
		public Runnable getRunnable(){
			return new Runnable() {
				
				@Override
				public void run() {
					command(null);					
				}
			};
		}
	}
	
	protected String parseStringIntoFixedSize(int numOfRows, int numOfColumns, String str){
		if(str == null)
			return null;
		
		StringBuilder sb = new StringBuilder("<html>");
		String[] strArr = str.split(" ");
		int numOfColumnsLeft = numOfColumns;
		for(int i = 0; i < strArr.length; i++){
			if(i == 0)
				sb.append(strArr[0]);
			else{
				if((strArr[i].length()+1) <= numOfColumnsLeft)/*plus 1 for the " "*/
					sb.append(" ");
				else{
					numOfRows--;
					if(numOfRows <= 0)
						break;/*There is a chance that we haven't appended all data!*/
					else{
						sb.append("<br>");
						numOfColumnsLeft = numOfColumns;
					}
				}
				
				numOfColumnsLeft = numOfColumnsLeft - strArr[i].length();
				sb.append(strArr[i]);
			}
		}
		/*We have filled up to the latest row with the data
		 that we need, lets see if there are more rows to fill up*/
		while(--numOfRows > 0){
			sb.append("<br>");
		}
		
		sb.append("</html>");
		return sb.toString();
		
	}

}
